home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */
- /* This program may be used, copied, modified, and redistributed freely */
- /* for noncommercial purposes, so long as this notice remains intact. */
-
- #pragma comment(exestr, "@(#) xconq.c 12.1 95/05/09 ")
-
- /* RCS $Header: xconq.c,v 1.4 88/07/17 17:11:08 shebs Exp $ */
-
- /* Unlike most multi-player network programs, xconq does not use multiple */
- /* processes. Instead, it relies on the network capability inherent in */
- /* some graphical interfaces (such as X) to open up multiple displays. */
-
- #include "config.h"
- #include "misc.h"
- #include "period.h"
- #include "side.h"
- #include "unit.h"
- #include "map.h"
- #include "global.h"
- #include "version.h"
-
- /* This is for extremely simple menus to be used with novice players. */
-
- typedef struct menu_entry {
- char *name;
- char *description;
- } MenuEntry;
-
- extern int nummaps; /* needed to initialize number of maps read */
-
- /* Definitions of globally global variables. */
-
- Global global; /* important (i.e. saved/restored) globals */
-
- MenuEntry mapmenu[MAXMAPMENU]; /* list of mapfiles to suggest to players */
-
- bool givenseen = FALSE; /* true if world known */
- bool Debug = FALSE; /* the debugging flag itself. */
- bool Build = FALSE; /* magic flag for scenario-builders */
- bool Freeze = FALSE; /* keeps machine players from moving */
- bool humans[MAXSIDES]; /* flags for which players are human or machine */
-
- char *programname; /* name of the main program */
- char *rawfilenames[MAXLOADED]; /* names specified on cmd line */
- char version[] = VERSION; /* version string */
- char *hosts[MAXSIDES]; /* names of displays for each side */
- char spbuf[BUFSIZE]; /* main printing buffer */
- char tmpbuf[BUFSIZE]; /* a temp buffer */
-
- int numfiles = 0; /* number of data files asked for */
- int numgivens = 0; /* number of sides given on cmd line */
- int numhumans = 0; /* number of bona-fide human players */
- int givenwidth = DEFAULTWIDTH; /* requested width for a random map */
- int givenheight = DEFAULTHEIGHT; /* requested height for a random map */
- int giventime = 0; /* requested time for individual turns */
-
- /* Indices of groups of mapfile types mentioned in collection of mapfiles. */
-
- int scnstart, scnend, perstart, perend, mapstart, mapend;
-
- Side *winner = NULL; /* the winning side (allies are also winners) */
-
- /* Where it all begins... the main program's primary duty is command line */
- /* interpretation, it hands off for everything else. */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- bool eopt = FALSE, ask = FALSE;
- char ch;
- int i, numenemies;
-
- programname = argv[0];
- nummaps = 0;
- for (i = 0; i < MAXLOADED; ++i) rawfilenames[i] = "";
- add_default_player();
- for (i = 1; i < argc; ++i) {
- if ((argv[i])[0] == '-') {
- ch = (argv[i])[1];
- if (Debug) printf("-%c\n", ch);
- switch (ch) {
- case 'A':
- if (i+1 >= argc) usage_error();
- add_player(FALSE, argv[++i]);
- break;
- case 'B':
- Build = TRUE;
- Freeze = TRUE;
- break;
- case 'D':
- Debug = TRUE;
- break;
- case 'e':
- if (i+1 >= argc) usage_error();
- eopt = TRUE;
- numenemies = atoi(argv[++i]);
- while (numenemies-- > 0) add_player(FALSE, NULL);
- break;
- case 'm':
- if (i+1 >= argc) usage_error();
- make_pathname(NULL, argv[++i], "map", spbuf);
- rawfilenames[numfiles++] = copy_string(spbuf);
- break;
- case 'M':
- if (i+2 >= argc) usage_error();
- givenwidth = atoi(argv[++i]);
- givenheight = atoi(argv[++i]);
- break;
- case 'p':
- if (i+1 >= argc) usage_error();
- make_pathname(NULL, argv[++i], "per", spbuf);
- rawfilenames[numfiles++] = copy_string(spbuf);
- break;
- case 'r':
- if (numgivens > 1) {
- fprintf(stderr, "Warning: -r is resetting the list of\n");
- fprintf(stderr, "players already given in the command.\n");
- }
- numgivens = numhumans = 0;
- break;
- case 's':
- if (i+1 >= argc) usage_error();
- make_pathname(NULL, argv[++i], "scn", spbuf);
- rawfilenames[numfiles++] = copy_string(spbuf);
- break;
- case 't':
- if (i+1 >= argc) usage_error();
- /* Converting to seconds for internal use */
- giventime = 60 * atoi(argv[++i]);
- break;
- case 'v':
- givenseen = TRUE;
- break;
- case 'x':
- ask = TRUE;
- break;
- default:
- usage_error();
- }
- } else {
- /* We just have a host name for a human player */
- add_player(TRUE, argv[i]);
- }
- }
- /* Put in a single machine opponent if no -e and no human opponents */
- if (!eopt && numgivens == 1) add_player(FALSE, NULL);
- /* Say hi to the user */
- printf("\n Welcome to XCONQ version %s\n\n", version);
- maybe_dump_news();
- /* If no command-line options supplied, work interactively */
- if (ask) {
- if (read_menu_file()) {
- ask_about_mapfiles();
- } else {
- printf("No period/map/scenario menus available.\n");
- }
- ask_about_players();
- }
- /* While away the hours setting everything up */
- init_random(-1);
- init_sides();
- init_units();
- init_game();
- init_movement();
- init_displays();
- init_mplayers();
- init_sighandlers();
- /* Relatively low chance of screwup now, so safe to delete saved game */
- if (saved_game()) remove_saved_game();
- /* Play! */
- play();
- }
-
- /* Complain and leave if command is garbage. */
-
- usage_error()
- {
- fprintf(stderr, "Usage: %s [display] [-A display] [-B] [-e n] [-m name]\n",
- programname);
- fprintf(stderr, " [-M w h] [-p name] [-r] [-s name] [-t n] [-v] [-x]\n");
- exit(1);
- }
-
- /* Add a player into the array of players, making sure of no overflow. */
- /* Fail if so, players probably made a typo on command line - help them */
- /* out with a list of what players had been included. */
-
- add_player(ahuman, host)
- bool ahuman;
- char *host;
- {
- if (numgivens >= MAXSIDES) {
- fprintf(stderr, "At most %d player sides allowed!\n", MAXSIDES);
- fprintf(stderr, "(Valid ones were ");
- list_players(stderr);
- fprintf(stderr, ")\n");
- exit(1);
- }
- hosts[numgivens] = host;
- humans[numgivens] = ahuman;
- numgivens++;
- if (ahuman) numhumans++;
- if (Debug) printf("Added %s player (%s)\n",
- (ahuman ? "human" : "machine"), (host ? host : ""));
- }
-
- /* The main loop that cycles through the turns and the phases. */
- /* Always departs through game end phase (or by core dump :-( ). */
-
- play()
- {
- while (TRUE) {
- init_turn_phase();
- spy_phase();
- disaster_phase();
- build_phase();
- flush_dead_units();
- supply_phase();
- sort_units(FALSE);
- movement_phase();
- consumption_phase();
- flush_dead_units();
- game_end_phase();
- }
- }
-
- /* Do random small things to get the turn started, including resetting */
- /* some side vars that could get things confused, if set wrong. */
- /* Most movement-related things should get set later, at beginning of */
- /* move phase. */
-
- init_turn_phase()
- {
- Side *side;
-
- ++global.time;
- if (Debug) printf("##### TURN %d #####\n", global.time);
- for_all_sides(side) {
- wedge_mode(side);
- show_timemode(side);
- if (giventime > 0) update_clock(side);
- flush_input(side);
- flush_output(side);
- }
- }
-
- /* The win/lose phase assesses the sides' performance during that turn, */
- /* and can end the game. In fact, it is the only way to get out, except */
- /* for the quick exit command. With multiple mixed players, there are a */
- /* number of possibilities at game end, such as an all machine win. Also, */
- /* it is possible for all players to lose simultaneously! */
-
- game_end_phase()
- {
- bool enemiesleft = FALSE;
- int sidesleft = 0, testrslt;
- Side *side, *side2, *survivor;
-
- if (Debug) printf("Entering game end phase\n");
- /* See if any sides have won or lost */
- if (global.time >= global.endtime) {
- notify_all("The end of the world has arrived!");
- for_all_sides(side) side_loses(side, NULL);
- } else if (Build) {
- /* No winning or losing while building */
- } else {
- for_all_sides(side) {
- if (!side->lost) {
- if (side->timedout) {
- notify_all("The %s ran out of time!",
- plural_form(side->name));
- side_loses(side, NULL);
- } else if (all_units_gone(side)) {
- notify_all("The %s have been wiped out!",
- plural_form(side->name));
- side_loses(side, NULL);
- } else {
- testrslt = condition_true(side);
- if (testrslt > 0) side_wins(side);
- if (testrslt < 0) side_loses(side, NULL);
- }
- }
- }
- }
- /* See if any opposing sides left */
- for_all_sides(side) {
- for_all_sides(side2) {
- if (!side->lost && !side2->lost && enemy_side(side, side2))
- enemiesleft = TRUE;
- }
- if (!side->lost) {
- survivor = side;
- sidesleft++;
- }
- }
- /* Decide if the game is over */
- if (numsides == 1) {
- /* A one-player game can't end */
- } else if (sidesleft == 0) {
- exit_xconq();
- } else if (!enemiesleft) {
- winner = survivor;
- if (sidesleft == 1) {
- notify(winner, "You have prevailed over your enemies!");
- } else {
- for_all_sides(side) {
- if (!side->lost) {
- notify(side, "Your alliance has defeated its enemies!");
- }
- }
- }
- wait_to_exit(winner);
- exit_xconq();
- } else {
- /* Game isn't over yet */
- }
- }
-
- /* Quicky test for absence of all units. */
-
- all_units_gone(side)
- Side *side;
- {
- int u;
-
- for_all_unit_types(u) if (side->units[u] > 0) return FALSE;
- return TRUE;
- }
-
- /* Check out all the winning and losing conditions, returning a flag */
- /* on which, if any, are true. The three return values are 1 for win, */
- /* -1 for lose, and 0 for undecided. */
-
- condition_true(side)
- Side *side;
- {
- int i, u, r, amt = 0;
- Unit *unit;
- Condition *cond;
-
- for (i = 0; i < global.numconds; ++i) {
- cond = &(global.winlose[i]);
- if ((between(cond->starttime, global.time, cond->endtime)) &&
- (cond->sidesn == -1 || cond->sidesn == side_number(side))) {
- switch (cond->type) {
- case TERRITORY:
- for_all_unit_types(u) {
- amt += side->units[u] * utypes[u].territory;
- }
- if (cond->win) {
- if (amt >= cond->n) {
- notify_all("The %s have enough territory to win!",
- plural_form(side->name));
- return 1;
- }
- } else {
- if (amt < cond->n) {
- notify_all("The %s don't have enough territory!",
- plural_form(side->name));
- return -1;
- }
- }
- break;
- case UNITCOUNT:
- if (cond->win) {
- for_all_unit_types(u) {
- if (side->units[u] < cond->units[u]) return 0;
- }
- notify_all("The %s have enough units to win!",
- plural_form(side->name));
- return 1;
- } else {
- for_all_unit_types(u) {
- if (side->units[u] > cond->units[u]) return 0;
- }
- notify_all("The %s don't have the required units!",
- plural_form(side->name));
- return -1;
- }
- break;
- case RESOURCECOUNT:
- if (cond->win) {
- for_all_resource_types(r) {
- if (side->resources[r] < cond->resources[r]) return 0;
- }
- notify_all("The %s have enough resources to win!",
- plural_form(side->name));
- return 1;
- } else {
- for_all_resource_types(r) {
- if (side->resources[r] > cond->resources[r]) return 0;
- }
- notify_all("The %s don't have the required resources!",
- plural_form(side->name));
- return -1;
- }
- break;
- case POSSESSION:
- unit = unit_at(cond->x, cond->y);
- if (cond->win) {
- if (unit != NULL && unit->side == side) {
- notify_all("The %s have got hex %d,%d!",
- plural_form(side->name), cond->x, cond->y);
- return 1;
- }
- } else {
- if (unit == NULL || unit->side != side) {
- notify_all("The %s don't have hex %d,%d!",
- plural_form(side->name), cond->x, cond->y);
- return -1;
- }
- }
- break;
- default:
- case_panic("condition type", cond->type);
- break;
- }
- }
- }
- return 0;
- }
-
- /* Winning is defined as not losing :-) */
-
- side_wins(side)
- Side *side;
- {
- Side *side2;
-
- for_all_sides(side2) {
- if (!allied_side(side, side2)) side_loses(side2, NULL);
- }
- }
-
- /* Be rude to losers and give all their units to their defeaters (might */
- /* not be one, so units become neutral or dead). Give the loser a chance */
- /* to study the screen, mark the side as lost, then pass on this detail to */
- /* all the other sides and remove spurious leftover images of units. */
-
- side_loses(side, victor)
- Side *side, *victor;
- {
- int ux, uy;
- Unit *unit;
- Side *side2;
-
- notify(side, "You lost, %s!", (flip_coin() ? "sucker" : "turkey"));
- if (active_display(side)) {
- wait_to_exit(side);
- close_display(side);
- wedge_mode(side);
- }
- for_all_units(unit) {
- if (alive(unit) && unit->side == side) {
- ux = unit->x; uy = unit->y;
- unit_changes_side(unit, victor, CAPTURE, SURRENDER);
- all_see_hex(ux, uy);
- }
- }
- side->lost = TRUE;
- for_all_sides(side2) {
- if (active_display(side2)) {
- remove_images(side2, side_number(side));
- if (side != side2) show_all_sides(side2);
- }
- }
- }
-
- /* Leave screen up so everybody can study it, and allow any input to take */
- /* it down. OK to freeze all the other sides at once here. */
-
- wait_to_exit(side)
- Side *side;
- {
- if (humanside(side) && active_display(side)) {
- sprintf(side->promptbuf, "[Do anything to exit]");
- show_prompt(side);
- freeze_wait(side);
- }
- }
-
- /* This routine should be called before any sort of non-death exit. */
- /* Among other things, it updates the statistics. */
-
- exit_xconq()
- {
- int n = 0;
- Unit *unit;
- Side *side;
-
- close_displays();
- /* Need to kill off all units to finish up statistics */
- for_all_units(unit) kill_unit(unit, (winner ? VICTOR : SURRENDER));
- print_statistics();
- /* Offer a one-line comment on the game and then leave */
- for_all_sides(side) if (!side->lost) n++;
- switch (n) {
- case 0:
- printf("\nEverybody lost!\n\n");
- break;
- case 1:
- printf("\nThe %s (%s) won!\n\n",
- plural_form(winner->name),
- (winner->host ? winner->host : "machine"));
- break;
- default:
- sprintf(spbuf, "");
- for_all_sides(side) {
- if (!side->lost) {
- if (strlen(spbuf) > 0) {
- sprintf(tmpbuf, "/");
- strcat(spbuf, tmpbuf);
- }
- sprintf(tmpbuf, "%s", side->name);
- strcat(spbuf, tmpbuf);
- if (side->host) {
- sprintf(tmpbuf, "(%s)", side->host);
- strcat(spbuf, tmpbuf);
- }
- }
- }
- printf("\nThe %s alliance won!\n\n", spbuf);
- break;
- }
- exit(0);
- }
-
- /* Shut down displays - should be done before any sort of exit. */
-
- close_displays()
- {
- Side *side;
-
- for_all_sides(side) if (active_display(side)) close_display(side);
- }
-
- /* This is to find out how everybody did. */
-
- print_statistics()
- {
- Side *side;
- FILE *fp;
-
- #ifdef STATISTICS
- if ((fp = fopen(STATSFILE, "w")) != NULL) {
- for_all_sides(side) {
- print_side_results(fp, side);
- print_unit_record(fp, side);
- print_combat_results(fp, side);
- if (side->next != NULL) fprintf(fp, "\f\n");
- }
- fclose(fp);
- } else {
- fprintf(stderr, "Can't open statistics file \"%s\"\n", STATSFILE);
- }
- #endif /* STATISTICS */
- }
-
- /* Read in a file in a special format - basically lines describing mapfiles */
- /* with markers for various types in between. Returns true if the file */
- /* was actually found. */
-
- read_menu_file()
- {
- int i, j;
- char *tmp;
- FILE *fp;
-
- scnstart = scnend = perstart = perend = mapstart = mapend = 0;
- make_pathname(XCONQLIB, MAPFILEFILE, NULL, spbuf);
- if ((fp = fopen(spbuf, "r")) != NULL) {
- for (i = 0; i < MAXMAPMENU; ++i) {
- fscanf(fp, "%s", tmpbuf);
- mapmenu[i].name = copy_string(tmpbuf);
- tmp = read_line(fp);
- for (j = 0; tmp[j] != '\0'; ++j) if (tmp[j] != ' ') break;
- mapmenu[i].description = tmp + j;
- if (strcmp(mapmenu[i].name, "Scenarios") == 0) {
- scnstart = i+1;
- } else if (strcmp(mapmenu[i].name, "Periods") == 0) {
- scnend = i-1;
- perstart = i+1;
- } else if (strcmp(mapmenu[i].name, "Maps") == 0) {
- perend = i-1;
- mapstart = i+1;
- } else if (strcmp(mapmenu[i].name, "End") == 0) {
- mapend = i-1;
- break;
- }
- }
- fclose(fp);
- return TRUE;
- } else {
- return FALSE;
- }
- }
-
- /* Display menus of mapfiles. Player can either choose a single scenario */
- /* or a period + map (but no guarantees that map will work with period!) */
-
- ask_about_mapfiles()
- {
- char *name;
-
- if (file_menu(scnstart, scnend, "scenario",
- "Enter a number, or hit return to look at maps and periods.",
- &name)) {
- make_pathname(NULL, name, "scn", spbuf);
- rawfilenames[numfiles++] = copy_string(spbuf);
- } else {
- if (file_menu(perstart, perend, "historical period",
- "Enter a number, or hit return for the standard period.",
- &name)) {
- make_pathname(NULL, name, "per", spbuf);
- rawfilenames[numfiles++] = copy_string(spbuf);
- } else {
- printf("\nYou will get the standard period.\n");
- }
- if (file_menu(mapstart, mapend, "map",
- "Enter a number, or hit return for a random map.",
- &name)) {
- make_pathname(NULL, name, "map", spbuf);
- rawfilenames[numfiles++] = copy_string(spbuf);
- } else {
- printf("\nYou will get a random map.\n");
- }
- }
- }
-
- /* Given an array of names, return success or failure of choice and maybe */
- /* one of the names. */
-
- file_menu(start, end, prompt, help, rslt)
- int start, end;
- char *prompt, *help, **rslt;
- {
- int i, ans;
-
- printf("\n");
- if (start > end) return FALSE;
- for (i = start; i <= end; ++i) {
- printf("[%d] %-14s %s\n",
- i - start + 1, mapmenu[i].name, mapmenu[i].description);
- }
- while (TRUE) {
- printf("\nChoose a %s: ", prompt);
- gets(spbuf);
- sscanf(spbuf, "%d", &ans);
- ans += start - 1;
- if (ans >= start && ans <= end) {
- *rslt = mapmenu[ans].name;
- return TRUE;
- } else if (iindex('?', spbuf) >= 0) {
- printf("%s\n", help);
- } else if (strlen(spbuf) == 0) {
- return FALSE;
- } else {
- printf("Garbled answer! Try again...\n");
- }
- }
- }
-
- /* This allows fairly general setup for a collection of players. There */
- /* should be facilities for removing as well as adding... */
-
- ask_about_players()
- {
- int i, n;
-
- while (TRUE) {
- printf("\nPlayers so far: ");
- list_players(stdout);
- printf("\n\nAny others? [number or hostname]: ");
- gets(spbuf);
- if (strlen(spbuf) == 0) {
- return;
- } else if (strcmp(spbuf, "r") == 0) {
- numgivens = 0;
- } else if (iindex('?', spbuf) >= 0) {
- printf("You can make various combinations of players,\n");
- printf("by giving either a number of machine or a host name.\n");
- printf("`r' clears the list; hit return when done.\n");
- } else if (numgivens < MAXSIDES) {
- if (sscanf(spbuf, "%d", &n) == 1) {
- n = max(0, min(n, MAXSIDES - numgivens));
- for (i = 0; i < n; ++i) add_player(FALSE, NULL);
- } else {
- add_player(TRUE, copy_string(spbuf));
- }
- } else {
- printf("Can't add anybody else; 'r' starts over.\n");
- }
- }
- }
-
- /* List all the specified players briefly. */
-
- list_players(fp)
- FILE *fp;
- {
- int i;
-
- if (numgivens > 0) {
- fprintf(fp, "%s", (hosts[0] ? hosts[0] : "machine"));
- for (i = 1; i < numgivens; ++i) {
- fprintf(fp, ", %s", (hosts[i] ? hosts[i] : "machine"));
- }
- } else {
- fprintf(fp, "no players defined.");
- }
- }
-